package com.company.project.core.aop;



import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;

/**
 * Created by admin on 2017/3/13.
 */
@Aspect
@Component
public class SystemLogAspect  {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);

    private static final long ONE_SECOND = 1000;

    public SystemLogAspect() {
        logger.info("SystemLogAspect construct method");
    }

    //Service层切点
    @Pointcut("execution(public * com.company.project.service.impl.*ServiceImpl.*(..))")
    public void serviceAspect() {
    }

    //Controller层切点
    @Pointcut("execution(public * com.company.project.web.*Controller.*(..)) ")
    public void controllerAspect() {
    }

    @Before("controllerAspect()")
    public void doControllerBefore(JoinPoint joinPoint) {

        String clazz = joinPoint.getTarget().getClass().getName();
        String method = joinPoint.getSignature().getName();
        logger.info("用户：{}[{}]请求方法{}.{}()开始。",  clazz, method);
        logger.info("请求参数:{}。");
    }

    @After("controllerAspect()")
    public void afterControllerBefore(JoinPoint joinPoint) {
//        String userId = getUserDetail().getId();
//        String userName = getUserDetail().getDisplayName();
        String clazz = joinPoint.getTarget().getClass().getName();
        String method = joinPoint.getSignature().getName();
        logger.info("用户：{}[{}]请求方法：{}.{}()结束。",  clazz, method);

    }

    @AfterReturning(value = "controllerAspect() && !execution(* *.export(..))", returning = "retValue")
    public void afterControllerBefore(JoinPoint joinPoint, Object retValue) {
        Object result = retValue;
        if(retValue!=null&&! (retValue instanceof String)){
            result = new JSONObject(retValue);
        }
        logger.info("返回结果:{}。", result);
    }


    @AfterThrowing(value = "controllerAspect()", throwing = "e")
    public void afterControllerThrowingMethod(JoinPoint joinPoint, Exception e) {
        this.setThrowException(joinPoint, e);
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     */
    public static String getMethodDescription(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        int argLength = joinPoint.getArgs().length;
        Method[] methods = joinPoint.getTarget().getClass().getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName) && method.getParameterCount() == argLength) {
//                description = method.getAnnotation(ApiOperation.class).value();
                break;
            }
        }
        return description;
    }

    private void setThrowException(JoinPoint joinPoint, Exception e) {
        JSONArray params = getParams(joinPoint);
        try {
            String exception = e.getClass().getName();
            String clazz = joinPoint.getTarget().getClass().getName();
            String method = joinPoint.getSignature().getName();
            String description = getMethodDescription(joinPoint);

            logger.error("=============== 异常通知开始 ===============");
            logger.error("异常代码:{}", exception);
            logger.error("异常信息:{}", e);
            logger.error("异常方法:{}.{}()", clazz, method);
            logger.error("方法描述:{}", description);
            logger.error("请求参数:{}", params.toString());
            logger.error("=============== 异常通知结束 ===============");
        } catch (Exception ex) {
            logger.error("========== 处理异常通知时出错了 ==========");
            logger.error("异常信息:{}", ex);
        }
    }

    private JSONArray getParams(JoinPoint joinPoint) {
        JSONArray params = new JSONArray();
        for (Object arg : joinPoint.getArgs()) {
            //去除request、response和httpSession
            if (!(arg instanceof ServletRequest) && !(arg instanceof ServletResponse) && !(arg instanceof HttpSession) && !(arg instanceof BindingResult)) {
                if (arg instanceof String) {
                    params.put(arg);
                } else {
                    params.put(new JSONObject(arg));
                }
            }
        }
        return params;
    }

    @Around("controllerAspect()")
    public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 开始执行时间
        long startTime = System.currentTimeMillis();
        Object obj = joinPoint.proceed(joinPoint.getArgs());
        // 结束执行时间
        long endTime = System.currentTimeMillis();

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
        // 打印耗时的信息
        this.printExecTime(methodName, startTime, endTime);
        return obj;
    }

    /**
     * 打印方法执行耗时的信息，如果超过了一定的时间，才打印
     *
     * @param methodName
     * @param startTime
     * @param endTime
     */
    private void printExecTime(String methodName, long startTime, long endTime) {
        long diffTime = endTime - startTime;
        if (diffTime > ONE_SECOND) {
            logger.info("执行{}方法耗时{}ms", methodName, diffTime);
        }
    }

}
